using System;
using System.Collections;
using System.Collections.Specialized;
using System.Data;
using System.Web.UI;
using System.Web.UI.WebControls;
using PHSRAG.Insight21.Common.Pages;
using PHSRAG.Insight21.Context;
using PHSRAG.Insight21.InsightUtilities;
using PHSRAG.Insight21.Policy;
using PHSRAG.Security.Enumerations;
using PHSRAG.Utility;
using Constants = PHSRAG.Insight21.InsightUtilities.Constants;

namespace PHSRAG.Insight21.Admin
{
    /// <summary>
    /// The AdminSearchUsers page allows users to search for domain user information.
    /// </summary>
    public partial class AdminSearchUsers : BasePage
    {
        #region Constants

        private const string CurrentPageIndexKey = "CurrentPageIndex_F940A82C-F599-4863-8243-A1351D9C7E5B";
        private const string FirstNameKey = "FirstName_FA045FFB-90BE-4f57-AA3A-4E8907DF3A26";
        private const int IncludeUnregisteredUsersBit = 1;
        private const string LastNameKey = "LastName_B2C6AD9A-0C60-4182-B9AE-982A8B59B8E3";
        private const string SortColumnKey = "SortColumn_8DD4FB62-755A-418f-B73A-07E79FA2A5B5";
        private const string SortDirectionKey = "SortAscending_8DD4FB62-755A-418f-B73A-07E79FA2A5B5";
        private const string UsernameKey = "Username_6A94E829-E0AA-4847-A447-EE1F09055CCE";

        #endregion

        #region Instance Variables (Private)

        private ICachePolicy objectCachePolicy;
        private ICachePolicy pageCachePolicy;

        #endregion

        #region Private Enumeration

        /// <summary>
        /// Users DataGrid columns
        /// </summary>
        private enum UsersDataGridColumns
        {
            ///<summary>Index of the column used to display a user's name</summary>
            Name
            ///<summary>Index of the column used to display a user's Username</summary>
            ,
            Username
            ///<summary>Index of the column used to display a user's Organization</summary>
            ,
            Organization
            ///<summary>Index of the column used to display a user's Registered</summary>
            ,
            Registered
            ///<summary>Index of the column used to display a user's DomainStatus</summary>
            ,
            DomainStatus
            ///<summary>Index of the column used to display a user's DomainStatus</summary>
            ,
            PersonId
            ///<summary>Index of the column used to display a user's DomainStatus</summary>
            ,
            LastName
            ///<summary>Index of the column used to display a user's DomainStatus</summary>
            ,
            FirstName
        }

        #endregion

        #region Private Properties

        /// <summary>
        /// Private boolean property used to get/set if the sort order is ascending or descending.
        /// This value is stored as a name / value pair in a hash table stored in the intra page cache under the 
        /// page's key.
        /// </summary>
        private bool SortAscending
        {
            get
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.SearchUsersPageKey) as Hashtable;

                if (h == null)
                {
                    return true;
                }

                object sortAscending = h[SortDirectionKey];
                return (sortAscending == null) ? true : (bool) sortAscending;
            }

            set
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.SearchUsersPageKey) as Hashtable ?? new Hashtable();

                h[SortDirectionKey] = value;
                pageCachePolicy.Cache(Keys.SearchUsersPageKey, h);
            }
        }

        /// <summary>
        /// Used to set/get the current sort column in use by the page's DataGrid.  
        /// This value is stored as a name / value pair in a hash table stored in the intra page cache under the 
        /// page's key.
        /// </summary>
        private UsersDataGridColumns SortColumn
        {
            get
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.SearchUsersPageKey) as Hashtable;

                if (h == null)
                {
                    return 0;
                }

                object sortColumn = h[SortColumnKey];
                return (sortColumn == null) ? 0 : (UsersDataGridColumns) sortColumn;
            }

            set
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.SearchUsersPageKey) as Hashtable ?? new Hashtable();

                h[SortColumnKey] = value;
                pageCachePolicy.Cache(Keys.SearchUsersPageKey, h);
            }
        }

        /// <summary>
        /// Private int property used to get/set the current page index.  
        /// PLEASE NOTE: that this property sets the dataGrid.CurrentPageIndex property.
        /// This value is stored as a name / value pair in a hash table stored in the intra page cache under the 
        /// page's key.
        /// </summary>
        /// 
        private int CurrentPageIndex
        {
            get
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.SearchUsersPageKey) as Hashtable;

                if (h == null)
                {
                    return 0;
                }

                object currentPageIndex = h[CurrentPageIndexKey];
                return (currentPageIndex == null) ? 0 : (int) currentPageIndex;
            }
            set
            {
                Hashtable h = pageCachePolicy.GetCachedObject(Keys.SearchUsersPageKey) as Hashtable ?? new Hashtable();

                h[CurrentPageIndexKey] = value;
                dataGrid.CurrentPageIndex = value;
                pageCachePolicy.Cache(Keys.SearchUsersPageKey, h);
            }
        }

        /// <summary>
        /// If the page does not already have a cached Users object, this propert will
        /// load a new Users object based on the user input search criteria and the 
        /// page's current sort direction.
        /// </summary>
        /// <returns></returns>
        private Users Users
        {
            get
            {
                Users users =
                    objectCachePolicy.GetCachedObject(Users.CacheKey) as Users;

                if (users == null)
                {
                    users = new Users(objectCachePolicy);

                    string sortInfo = (SortColumn == UsersDataGridColumns.Name)
                        ? String.Format("{0} {1}, {2} {1}",
                            Enum.GetName(typeof (UsersDataGridColumns), UsersDataGridColumns.LastName),
                            SortAscending ? Constants.SortViewAscending : Constants.SortViewDescending,
                            Enum.GetName(typeof (UsersDataGridColumns), UsersDataGridColumns.FirstName))
                        : String.Format("{0} {1}",
                            Enum.GetName(typeof (UsersDataGridColumns), SortColumn),
                            SortAscending ? Constants.SortViewAscending : Constants.SortViewDescending);

                    users.Load(DataAccess.GetConnectionString(),
                        sortInfo,
                        ResultSetType.IDsOnly,
                        userName.Text.Trim(),
                        lastName.Text.Trim(),
                        firstName.Text.Trim(),
                        IncludeUnregisteredUsersBit);

                    users.Cache();
                }
                return users;
            }
        }

        #endregion

        #region Event Handlers

        /// <summary>
        /// Occurs when the page is loaded.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void Page_Load(object sender, EventArgs e)
        {
            try
            {
                breadCrumb.Add("Search Users", "AdminSearchUsers.aspx", Settings.GetKeyValue("BreadCrumb.SummaryPageLevel", 1));
                dataGrid.PageSize = Settings.GetKeyValue("DataGrid.ItemsPerPage", 5);


                if (!IsPostBack && postbackGuard.IsValid)
                {
                    NameValueCollection queryString = Request.QueryString;

                    if (queryString != null && Convert.ToBoolean(queryString["showAsPopup"]))
                    {
                        headerSection.Visible = false;
                    }

                    UserContext userContext = (UserContext) objectCachePolicy.GetCachedObject(UserContext.CacheKey);

                    if (!userContext.IsKeyGiver &&
                        ((userContext.ModuleRoleBitmask & (int) ModuleRoleBit.SystemAdministrator) == 0) &&
                            ((userContext.ModuleRoleBitmask & (int) ModuleRoleBit.PrincipalInvestigator) == 0))
                    {
                        //don't let the user search for users and warn them they aren't authorized for admin
                        search.Visible = pagerAboveDatagrid.Visible = pagerBelowDatagrid.Visible = false;
                        throw new Exception("You are not authorized to use the Admin module");
                    }

                    Hashtable h = pageCachePolicy.GetCachedObject(Keys.SearchUsersPageKey) as Hashtable;
                    if (h != null)
                    {
                        userName.Text = pageCachePolicy.GetCachedObject(UsernameKey) as string;
                        lastName.Text = pageCachePolicy.GetCachedObject(LastNameKey) as string;
                        firstName.Text = pageCachePolicy.GetCachedObject(FirstNameKey) as string;
                        LoadDataGrid();

                        if (CurrentPageIndex == 0)
                        {
                            InitializePager();
                        }
                        else
                        {
                            ManageNavigationLinks();
                        }
                    }
                    else
                    {
                        dataGrid.Visible = pagerBelowDatagrid.Visible = pagerAboveDatagrid.Visible = false;
                    }
                }
            }
            catch (Exception ex)
            {
                annunciator.Message = ex.Message;
            }
        }

        /// <summary>
        /// This event occurs when the search button is selected. The page's datagrid
        /// is loaded with the search results.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnSearch(object sender, ImageClickEventArgs e)
        {
            Users.UnCache();
            annunciator.Message = "";

            breadCrumb.Add("Search Users", "AdminSearchUsers.aspx", Settings.GetKeyValue("BreadCrumb.SummaryPageLevel", 1));
            dataGrid.PageSize = Settings.GetKeyValue("DataGrid.ItemsPerPage", 5);

            LoadDataGrid();
            InitializePager();

            if (!pagerBelowDatagrid.Visible)
            {
                annunciator.Message = "No users were found that match your search criteria.";
            }

            pageCachePolicy.Cache(UsernameKey, userName.Text.Trim());
            pageCachePolicy.Cache(LastNameKey, lastName.Text.Trim());
            pageCachePolicy.Cache(FirstNameKey, firstName.Text.Trim());
        }

        /// <summary>
        /// Occurs when a column heading is selected on the page's DataGrid.  Current sort 
        /// column and direction are evaluated from the intra page cache and updated.  The datagrid
        /// is loaded with the resorted data.
        /// </summary>
        /// <param name="source">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnSort(object source, DataGridSortCommandEventArgs e)
        {
            ProcessSortCriteria(e.SortExpression);
            Users.UnCache();
            LoadDataGrid();
            InitializePager();
        }

        /// <summary>
        /// OnItemDataBound event of the dataGrid is used to format the following columns in the DataGrid:
        ///		Name = User.LastName, User.FirstName
        ///		Registered = Yes or No based on the User.Registered property.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnItemDataBound(object sender, DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Item || e.Item.ItemType == ListItemType.AlternatingItem)
            {
                //format the Name column
                LinkButton nameColumn = (LinkButton) e.Item.Cells[0].Controls[0];
                nameColumn.Text = String.Format("{0}, {1}", e.Item.Cells[6].Text, e.Item.Cells[7].Text);

                NameValueCollection queryString = Request.QueryString;

                if (queryString != null && queryString["urlReferrer"] != null && queryString["urlReferrer"] == "ResearchContactsManagement.aspx")
                {
                    nameColumn.Attributes["href"] = "";
                    nameColumn.Attributes.Add("onclick", "javascript:CloseDialog('" + nameColumn.Text + "', '" + e.Item.Cells[5].Text + "');");
                }

                //format the Registered column
                e.Item.Cells[3].Text = (Convert.ToBoolean(e.Item.Cells[3].Text)) ? "Yes" : "No";
            }
        }

        /// <summary>
        /// On Item Created event of the dataGrid is used to indicate the sort column and the sort order.
        /// It references the private properties for SortColumn and SortAscending and sets the appropriate
        /// web ding in the appropriate column of the grid header.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnItemCreated(object sender, DataGridItemEventArgs e)
        {
            if (e.Item.ItemType == ListItemType.Header)
            {
                Label sortSymbol = new Label();
                sortSymbol.Text = SortAscending ? Constants.SortAscendingText : Constants.SortDescendingText;
                sortSymbol.Font.Name = Constants.SortSymbolFontName;
                TableCell cell = e.Item.Cells[(int) SortColumn];

                if (cell != null)
                {
                    cell.Controls.Add(sortSymbol);
                }
            }
        }

        /// <summary>
        /// Occus when a button is clicked within the page's DataGrid control.  The personid
        /// is captured, cached in the intra page cache, and the client is redirected to the AdminUserProfile page.
        /// </summary>
        /// <param name="source">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnItemCommand(object source, DataGridCommandEventArgs e)
        {
            if (e.CommandName == "SelectName")
            {
                Hashtable dictionary = new Hashtable();
                dictionary.Add(Keys.PersonIdKey, Convert.ToInt32(e.Item.Cells[5].Text));
                pageCachePolicy.Cache(Keys.IncomingDataKey, dictionary);

                Page.Response.Redirect(Settings.GetKeyValue("SubNavigation.Admin.UserDetails.UserProfileURL", ""));
            }
        }

        /// <summary>
        /// Occurs when the FirstPage hyperlink is clicked on the pager.
        /// The page's DataGrid is reloaded with the data belonging to the first page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnFirstPage(object sender, CommandEventArgs e)
        {
            CurrentPageIndex = 0;
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Occurs when the LastPage hyperlink is clicked on the pager.
        /// The page's DataGrid is reloaded with the data belonging to the last page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnLastPage(object sender, CommandEventArgs e)
        {
            CurrentPageIndex = pagerAboveDatagrid.PageCount - 1;
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Occurs when the NextPage hyperlink is clicked on the pager. 
        /// Th page's DataGrid is reloaded with the data belonging to the next page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnNextPage(object sender, CommandEventArgs e)
        {
            ++CurrentPageIndex;
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Occurs when the PreviousPage hyperlink is clicked on the pager.
        /// The page's DataGrid is reloaded with the data belonging to the previous page.
        /// </summary>
        /// <param name="sender">The object raising an event.</param>
        /// <param name="e">Argument associated with the event</param>
        protected void OnPreviousPage(object sender, CommandEventArgs e)
        {
            --CurrentPageIndex;
            LoadDataGrid();
            ManageNavigationLinks();
        }

        /// <summary>
        /// Resets the subnavigation selected link(s) before the 
        /// header performs a redirect.
        /// </summary>
        /// <param name="sender">Control which rasied the event.</param>
        /// <param name="e">Arguments associated with the event.</param>
        protected void OnHeaderNavigate(object sender, CommandEventArgs e)
        {
            adminSubNavigation.Reset();
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Initializes the pager control's properties based on the visibility and number of items in the DataGrid.
        /// </summary>
        private void InitializePager()
        {
            dataGrid.Visible = pagerBelowDatagrid.Visible = pagerAboveDatagrid.Visible = (Users.GetDataContainer().Tables[0].Rows.Count > 0);

            if (pagerBelowDatagrid.Visible)
            {
                pagerBelowDatagrid.FirstRecord = pagerAboveDatagrid.FirstRecord = 1;
                pagerBelowDatagrid.LastRecord = pagerAboveDatagrid.LastRecord = Math.Min(dataGrid.PageSize, dataGrid.Items.Count);
                pagerBelowDatagrid.CurrentPage = pagerAboveDatagrid.CurrentPage = 1;
                CurrentPageIndex = 0;
                ManageNavigationLinks();
            }
        }

        /// <summary>
        /// This method is used to enable/disable the navigation links on the pager objects
        /// above and below the datagrid.
        /// </summary>
        private void ManageNavigationLinks()
        {
            pagerBelowDatagrid.EnableFirstPage = pagerAboveDatagrid.EnableFirstPage =
                pagerBelowDatagrid.EnablePreviousPage = pagerAboveDatagrid.EnablePreviousPage =
                    (CurrentPageIndex != 0);

            pagerBelowDatagrid.EnableLastPage = pagerAboveDatagrid.EnableLastPage =
                pagerBelowDatagrid.EnableNextPage = pagerAboveDatagrid.EnableNextPage =
                    (CurrentPageIndex != (pagerAboveDatagrid.PageCount - 1));
        }

        /// <summary>
        /// This method gets a list of person id's that could be loaded by this page.  It then 
        /// loads the relevant set of user objects into the datagrid.
        /// </summary>
        private void LoadDataGrid()
        {
            try
            {
                DataSet personIDs = Users.GetDataContainer();
                int usersCount = personIDs.Tables[0].Rows.Count;

                dataGrid.VirtualItemCount = pagerBelowDatagrid.RecordCount = pagerAboveDatagrid.RecordCount = usersCount;

                int pageCount = (pagerAboveDatagrid.RecordCount/dataGrid.PageSize) +
                    (((pagerAboveDatagrid.RecordCount%dataGrid.PageSize) == 0) ? 0 : 1);
                pagerBelowDatagrid.PageCount = pagerAboveDatagrid.PageCount = pageCount;
                pagerBelowDatagrid.CurrentPage = pagerAboveDatagrid.CurrentPage = CurrentPageIndex + 1;

                if (usersCount > 0)
                {
                    dataGrid.DataSource = GetUserList(personIDs);
                    dataGrid.DataBind();
                }
            }
            catch (Exception e)
            {
                annunciator.Message = ExceptionReport.GetExceptionInfo(e, false);
            }
        }

        /// <summary>
        /// If the page does not already have a cached ArrayList containing loaded user objects, this method will
        /// load a new arraylist based on the list of person ids passed in.  The pager information is evaluated to 
        /// determine the relevant subset of person id's to use to load individual user objects into the arraylist. 
        /// </summary>
        /// <param name="personIDs">DataSet containing the full list of PersonId's that could be loaded by the page's datagrid.</param>
        /// <returns></returns>
        private ArrayList GetUserList(DataSet personIDs)
        {
            ArrayList userList = new ArrayList();
            int startIndex = CurrentPageIndex*dataGrid.PageSize;
            int endIndex = Math.Min(startIndex + dataGrid.PageSize, personIDs.Tables[0].Rows.Count);
            pagerBelowDatagrid.FirstRecord = pagerAboveDatagrid.FirstRecord = startIndex + 1;
            pagerBelowDatagrid.LastRecord = pagerAboveDatagrid.LastRecord = endIndex;

            for (int i = startIndex; i < endIndex; ++i)
            {
                User user = new User(objectCachePolicy);
                user.Load(DataAccess.GetConnectionString(), (int) (personIDs.Tables[0].Rows[i]["PersonID"]));
                userList.Add(user);
            }
            return userList;
        }

        /// <summary>
        /// ProcessSortCriteria takes the sort expression from the dataGrid and handles toggling the
        /// sort direction, setting the sort column, and clearing the cache in preparation for a sort.
        /// </summary>
        /// 
        private void ProcessSortCriteria(string sortExpression)
        {
            // If a sort was clicked and the column was the same as the current sort column,
            // We must toggle the sort order and clear the cache if one exists already. 

            if (sortExpression != string.Empty)
            {
                if (Enum.GetName(typeof (UsersDataGridColumns), SortColumn) == sortExpression)
                {
                    SortAscending = !SortAscending;
                }

                SortColumn = (UsersDataGridColumns) Enum.Parse(typeof (UsersDataGridColumns), sortExpression);
            }
        }

        #endregion

        #region Web Form Designer generated code

        /// <summary>
        /// Initializes components used by the page.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnInit(EventArgs e)
        {
            InitializeComponent();
            base.OnInit(e);

            objectCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.CachePolicyKey];
            pageCachePolicy = (Policy.ICachePolicy) Session[Policy.Names.PageCachePolicyKey];
        }

        /// <summary>
        /// Required method for Designer support - do not modify
        /// the contents of this method with the code editor.
        /// </summary>
        private void InitializeComponent()
        {
        }

        #endregion
    }
}